package org.extremengine.demo;

import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Iterator;
import java.util.Map;

import javax.swing.JPanel;
import javax.swing.border.LineBorder;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.extremengine.EngineCallback;
import org.extremengine.TimerThread;
import org.extremengine.World;
import org.extremengine.common.Collidable;
import org.extremengine.common.RigidBody;
import org.extremengine.common.Terrain;
import org.extremengine.shape.Circle;
import org.extremengine.shape.ParallelRectangle;
import org.extremengine.shape.Rectangle;
import org.extremengine.shape.Shape;
import org.extremengine.shape.UnlimitedLine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 
 * @author yuxing
 * 
 */
public abstract class BasePanelWorld extends JPanel {
	
	protected int MARGIN = 20;

	protected int WORLD_WIDTH = 0;

	protected int WORLD_HEIGHT = 0;

	public World world;

	protected Graphics canvas;

	protected DemoCallback demoCallback;
	
	protected Terrain ground;
	protected Terrain ceiling;
	protected Terrain wallLeft;
	protected Terrain wallRight;
	
	
	public BasePanelWorld(DemoCallback callback) {
		super();
		
		this.demoCallback = callback;

		this.setBorder(new LineBorder(Color.RED, 1));

		this.setBackground(Color.BLACK);

		// 点击画面时世界开始运行
		this.addMouseListener(new MouseAdapter() {

			@Override
			public void mouseReleased(MouseEvent e) {
				System.out.println("Mouse Released");
//				if(world.getStatus() == TimerThread.STATUS_PAUSED) {
//					world.resume();
//				}
				// 由调用者处理物理世界运行状态变化
				demoCallback.onWorldStatusChanged(world.getStatus());
			}
		});

	}

	@Override
	public void paint(Graphics g) {
		super.paint(g);
		System.out.println("Init world for " + this.getClass().getName());
//		WORLD_WIDTH = this.getBounds().width;
//		WORLD_HEIGHT = this.getBounds().height;
		if (world == null) {
			initDemoWorld();
			try {
				demoCallback.onInited(world);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		System.out.println("paint terrain");
		this.drawTerrain(g);
	}

	/**
	 * 初始化World，不能在创建Panel时调用. this.getBounds()无法获得
	 */
	public void initDemoWorld() {

		WORLD_WIDTH = this.getBounds().width - MARGIN * 2;
		WORLD_HEIGHT = this.getBounds().height - MARGIN * 2;

		System.out.printf("Canvas bounds: %d, %d \r\n", WORLD_WIDTH, WORLD_HEIGHT);

		// 初始化世界
		world = new World(this.translate(WORLD_WIDTH), this.translate(WORLD_HEIGHT));
		world.setGravityAccelaration(9.8);

		// 创建地面和墙
		ground = world.createGround(this.translate(0), 0.5, 0.8);
		ceiling = world.createCeiling(this.translate(WORLD_HEIGHT), 0.5, 0.8);
		wallLeft = world.createWall(this.translate(0), 0.5, 0.5, UnlimitedLine.FREE_SPACE_POSITIVE);
		wallRight = world.createWall(this.translate(WORLD_WIDTH), 0.5, 0.5, UnlimitedLine.FREE_SPACE_NEGETIVE);

	}

	public void startDemoWorld() {
		System.out.println("Start world");
		final Component targetComponent = this;
		this.canvas = this.getGraphics();		
		
		this.setDoubleBuffered(true);
		
		if(this.world == null || this.world.getStatus() == TimerThread.STATUS_STOPED) {
			this.initDemoWorld();
		}

		// 世界开始运行，绘制世界和刚体、地形间碰撞后的处理。
		this.world.play(new EngineCallback() {

			Logger log = LoggerFactory.getLogger("callback");
			/**
			 * 处理每一帧的显示绘制。
			 */
			@Override
			public void onFrame(long t, Map coordinateMap) {
				Iterator it = coordinateMap.keySet().iterator();
				targetComponent.invalidate();
				canvas.setColor(Color.BLACK);
				canvas.fillRect(MARGIN, MARGIN, WORLD_WIDTH, WORLD_HEIGHT);

				drawTerrain(canvas);
				
				StringBuilder logBuf = new StringBuilder();
				logBuf.append(StringUtils.left("" + t + "    ", 5));

				for (; it.hasNext();) {
					Object key = it.next();
					RigidBody rb = (RigidBody) coordinateMap.get(key);
					drawRigidBody(canvas, rb);
					logBuf.append(rb.toString() + "    ");
				}

				// canvas.drawString("FPS: " + fps, 50, 50);
				targetComponent.validate();
				
				// 记录日志
				log.debug(logBuf.toString());
//				Iterator it = coordinateMap.values().iterator();
				
			}

			@Override
			public void onFps(long t, int fps) {
				// System.out.println("FPS: " + fps);
				try {
					demoCallback.onTita(t, fps);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}

			/**
			 * 处理刚体之间碰撞。
			 */
			@Override
			public void onCollision(long t, RigidBody rb1, RigidBody rb2) {
				log.info("Collision RB1:{} RB2:{}", rb1, rb2);
				handleCollision(t, rb1, rb2);
			}

			/**
			 * 处理刚体和地形碰撞。
			 */
			@Override
			public void onCollision(long t, RigidBody body, Terrain terrain) {
				log.info("Collision RB:{} TERRAIN:{}", body, terrain);
				System.out.println("Collision: Rigid Body hits Terrain");
				System.out.println("RB: " + body);
				System.out.println("TR: " + terrain);
			}

			@Override
			public void onStick(Collidable matter1, Collidable matter2) {
				System.out.println("Stick: ");
				System.out.println("Matter 1: " + matter1);
				System.out.println("Matter 2: " + matter2);
			}
			
			
		});
	}
	
	public void pauseDemoWorld() {
		world.pause();
	}
	
	public void resumeDemoWorld() {
		world.resume();
	}
	
	public void stopDemoWorld() {
		world.stop();
	}
	
	/**
	 * 处理刚体间碰撞
	 * @param t
	 * @param rb1
	 * @param rb2
	 */
	protected void handleCollision(long t, RigidBody rb1, RigidBody rb2) {
//		System.out.println("Collision: " + rb1 + " hits " + rb2);
		System.out.println("Collision, 2 Rigid Bodies");
		System.out.println("RB1: " + rb1);
		System.out.println("RB2: " + rb2);
	}

	/**
	 * 绘制地形
	 * @param canvas
	 */
	protected void drawTerrain(Graphics canvas) {
		if (canvas == null) {
			canvas = this.getGraphics();
		}
		canvas.setColor(Color.WHITE);

		// Ground
		canvas.drawLine(MARGIN, WORLD_HEIGHT + MARGIN, WORLD_WIDTH + MARGIN, WORLD_HEIGHT + MARGIN);
		// Ceiling
		canvas.drawLine(MARGIN, MARGIN, WORLD_WIDTH + MARGIN, MARGIN);
		// Walls
		canvas.drawLine(MARGIN, MARGIN, MARGIN, WORLD_HEIGHT + MARGIN);
		canvas.drawLine(WORLD_WIDTH + MARGIN, MARGIN, WORLD_WIDTH + MARGIN, WORLD_HEIGHT + MARGIN);
	}

	/**
	 * 像素值转换成物理值
	 * 
	 * @param pixel
	 * @return
	 */
	protected double translate(int pixel) {
		return pixel / 10.0;
	}

	/**
	 * 物理值转换成像素值
	 * 
	 * @param position
	 * @return
	 */
	protected int translate(double position) {
		return (int) (position * 10.0);
	}

	/**
	 * 物理坐标转换成像素位置
	 * 
	 * @param cordinate
	 * @return
	 */
	protected int[] translateCordinate(double[] cordinate) {
		if (ArrayUtils.isEmpty(cordinate) || cordinate.length != 2) {
			return new int[] { 0, 0 };
		}
		return new int[] { (int) (cordinate[0] * 10.0), (int) (cordinate[1] * -10.0), };
	}

	/**
	 * 绘制刚体
	 * @param canvas
	 * @param rb
	 */
	protected void drawRigidBody(Graphics canvas, RigidBody rb) {
		// System.out.println("Draw Rigid Body");
		if (canvas == null || rb == null) {
			return;
		}
		canvas.setColor(Color.WHITE);
		Shape shape = rb.getShape();
		if (shape instanceof Circle) {
			Circle c = (Circle) shape;
			int x = translate(c.getHeart().x - c.getRadius()) + MARGIN;
			int y = translate(c.getHeart().y + c.getRadius()) - MARGIN;
			int w = translate(c.getRadius() * 2);
			int h = translate(c.getRadius() * 2);
//			System.out.println("RENDER[" + rb.getName() + "]: x:" + x + " y:" + (WORLD_HEIGHT - y) + " w:" + w + " h:"
//					+ h);
			canvas.drawOval(x, WORLD_HEIGHT - y, w, h);
		}
		else if (shape instanceof ParallelRectangle) {
			ParallelRectangle pr = (ParallelRectangle) shape;
			int x = translate(pr.getX1()) + MARGIN;
			int y = translate(pr.getY2()) - MARGIN;
			int w = translate(pr.getX2() - pr.getX1());
			int h = translate(pr.getY2() - pr.getY1());
//			System.out.println("RENDER[" + rb.getName() + "]: x:" + x + " y:" + (WORLD_HEIGHT - y) + " w:" + w + " h:"
//					+ h);
			canvas.drawRect(x, WORLD_HEIGHT - y, w, h);
		}
		else if (rb.getShape() instanceof Rectangle) {
			Rectangle r = (Rectangle) rb.getShape();
		}
		else {

		}
	}
	
	/**
	 * Callback interface for demo.
	 * @author yuxing
	 *
	 */
	public static interface DemoCallback {
		
		/**
		 * World inited.
		 * @param world
		 */
		public void onInited(World world);
		
		/**
		 * Called every time unit(usually second).
		 * @param time
		 * @param fps
		 * @return
		 */
		public long onTita(long time, long fps);
		
		/**
		 * World's status changed.
		 * @param newStatus
		 */
		public void onWorldStatusChanged(int newStatus);

	}
}
